﻿/*
-----------------------------------------------------------------------------
This source file is part of Cell Cloud.

Copyright (c) 2009-2012 Cell Cloud Team - cellcloudproject@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "CellStableHeaders.h"
#include "CellIniFile.h"

#include <algorithm>
#include <iostream>
#include <fstream>

// Only valid if C++
#ifndef __cplusplus
	#error C++ compiler required.
#endif

// In the event you want to trace the calls can define _TRACE_CINIFILE
#ifdef _TRACE_CINIFILE
	#define _INIFILE_DEBUG
#endif

// _CRLF is used in the Save() function
// The class will read the correct data regardless of how the file linefeeds are defined <CRLF> or <CR>
// It is best to use the linefeed that is default to the system. This reduces issues if needing to modify
// the file with ie. notepad.exe which doesn't recognize unix linefeeds.
#ifdef _WIN32 // Windows default is \r\n
#ifdef _FORCE_UNIX_LINEFEED
	#define _CRLFA "\n"
	#define _CRLFW L"\n"
#else
	#define _CRLFA "\r\n"
	#define _CRLFW L"\r\n"
#endif

#else // Standard format is \n for unix
	#ifdef _FORCE_WINDOWS_LINEFEED
	#define _CRLFA "\r\n"
	#define _CRLFW L"\r\n"
#else
	#define _CRLFA "\n"
	#define _CRLFW L"\n"
#endif
#endif

// Helper Functions
void rtrim(std::string &str, const std::string& chars = " \t")
{
#ifdef _INIFILE_DEBUG
    std::cout <<  "rtrim()" << std::endl;
#endif
    str.erase(str.find_last_not_of(chars)+1);
}

void ltrim(std::string &str, const std::string& chars = " \t")
{
#ifdef _INIFILE_DEBUG
    std::cout <<  "ltrim()" << std::endl;
#endif
    str.erase(0, str.find_first_not_of(chars));
}

void trim(std::string& str , const std::string& chars = " \t")
{
#ifdef _INIFILE_DEBUG
    std::cout <<  "trim()" << std::endl;
#endif
    str.erase(str.find_last_not_of(chars)+1);
    str.erase(0, str.find_first_not_of(chars));
}

// Stream Helpers

std::ostream& operator<<(std::ostream& output, cell::IniFileA& obj)
{
    obj.save(output);
    return output;
}

std::istream& operator>>(std::istream& input, cell::IniFileA& obj)
{
    obj.load(input);
    return input;
}

std::istream& operator>>(std::istream& input, cell::IniMergeA merger)
{
    return merger(input);
}

namespace cell
{

const char* const IniFileA::LF = _CRLFA;

//-----------------------------------------------------------------------
IniFileA::IniFileA(void)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::IniFileA()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniFileA::~IniFileA()
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::~IniFileA()" << std::endl;
#endif
	removeAllSections();
}
//-----------------------------------------------------------------------
void IniFileA::save(std::ostream& output)
{
	std::string section;

	for (SecIndexA::iterator itr = mSections.begin(); itr != mSections.end(); ++itr)
	{
		section = "[" + (*itr)->getSectionName() + "]";

	#ifdef _INIFILE_DEBUG
		std::cout <<  "Writing Section " << section << std::endl;
	#endif

		output << section << _CRLFA;

		for (KeyIndexA::iterator klitr = (*itr)->mKeys.begin(); klitr != (*itr)->mKeys.end(); ++klitr)
		{
			std::string key = (*klitr)->getKeyName() + "=" + (*klitr)->getValue();
		#ifdef _INIFILE_DEBUG
			std::cout <<  "Writing Key [" << key << "]" << std::endl;
		#endif
			output << key << _CRLFA;
		}
	}
}
//-----------------------------------------------------------------------
bool IniFileA::save(const std::string& fileName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::save() - " << fileName << std::endl;
#endif

	std::ofstream output;

	output.open(fileName.c_str() , std::ios::binary);

	if (!output.is_open())
		return false;

	save(output);

	output.close();
	return true;
}
//-----------------------------------------------------------------------
void IniFileA::load(std::istream& input , bool merge)
{
	if (!merge)
		removeAllSections();

	IniSectionA* section = NULL;
	std::string read;
	enum { KEY , SECTION , COMMENT , OTHER };

	while (std::getline(input, read))
	{
		// Trim all whitespace on the left
		ltrim(read);
		// Trim any returns
		rtrim(read, "\n\r");

		if (!read.empty())
		{
			unsigned int nType = (read.find_first_of("[") == 0 && (read[read.find_last_not_of(" \t\r\n")] == ']')) ? SECTION : OTHER;
			nType = ( (nType == OTHER) && ( read.find_first_of("=") != std::string::npos && read.find_first_of("=") > 0 ) )? KEY : nType ;
			nType = ( (nType == OTHER) && ( read.find_first_of("#") == 0) ) ? COMMENT : nType ;

			switch (nType)
			{
			case SECTION:
			#ifdef _INIFILE_DEBUG
				std::cout <<  "Parsing: Secton - " << read << std::endl;
			#endif
				section = addSection(read.substr( 1 , read.size() - 2 ));
				break;

			case KEY:
				{
				#ifdef _INIFILE_DEBUG
					std::cout <<  "Parsing: Key - " << read << std::endl;
				#endif
					// Check to ensure valid section... or drop the keys listed
					if (section)
					{
						size_t iFind = read.find_first_of("=");
						std::string key = read.substr(0,iFind);
						std::string value = read.substr(iFind + 1);
						IniKeyA* pKey = section->addKey(key);
						if (pKey)
						{
						#ifdef _INIFILE_DEBUG
							std::cout <<  "Parsing: Key Value - " << value << std::endl;
						#endif
							pKey->setValue(value);
						}
					}
				}
				break;
			case COMMENT:
			#ifdef _INIFILE_DEBUG
				std::cout <<  "Parsing: Comment - " << read << std::endl;
			#endif
				break;
			case OTHER:
			#ifdef _INIFILE_DEBUG
				std::cout <<  "Parsing: Other - " << read << std::endl;
			#endif
				break;
			}
		}
	}
}
//-----------------------------------------------------------------------
bool IniFileA::load(const std::string& fileName , bool merge)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::load() - " << fileName << std::endl;
#endif

	std::ifstream input;

	input.open(fileName.c_str() , std::ios::binary);

	if (!input.is_open())
		return false;

	load(input , merge);

	input.close();
	return true;
}
//-----------------------------------------------------------------------
const SecIndexA& IniFileA::getSections(void) const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::getSections()" << std::endl;
#endif
	return mSections;
}
//-----------------------------------------------------------------------
IniSectionA* IniFileA::getSection(const std::string& section) const
{
#ifdef _INIFILE_DEBUG
	std::cout << "IniFileA::getSection()" << std::endl;
#endif
	std::string sec = section;
	trim(sec);
	SecIndexA::const_iterator itr = _find_sec(sec);
	if (itr != mSections.end())
		return *itr;
	return NULL;
}
//-----------------------------------------------------------------------
IniSectionA* IniFileA::addSection(const std::string& section)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::AddSection()" << std::endl;
#endif
	std::string sec = section;
	trim(sec);
	SecIndexA::const_iterator itr = _find_sec(sec);
	if (itr == mSections.end())
	{
		// Note constuctor doesn't trim the string so it is trimmed above
		IniSectionA* pSection = CELL_NEW IniSectionA(this, sec);
		mSections.insert(pSection);
		return pSection;
	}
	else
	{
		return *itr;
	}
}
//-----------------------------------------------------------------------
std::string IniFileA::getKeyValue(const std::string& section, const std::string& key) const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::getKeyValue()" << std::endl;
#endif
	std::string value;
	IniSectionA* pSec = getSection(section);
	if (NULL != pSec)
	{
		IniKeyA* pKey = pSec->getKey(key);
		if (NULL != pKey)
			value = pKey->getValue();
	}
	return value;
}
//-----------------------------------------------------------------------
void IniFileA::setKeyValue(const std::string& section, const std::string& key, const std::string& value)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::setKeyValue()" << std::endl;
#endif
	IniSectionA* pSec = addSection(section);
	if (NULL != pSec)
	{
		IniKeyA* pKey = pSec->addKey(key);
		if (NULL != pKey)
			pKey->setValue(value);
	}
}
//-----------------------------------------------------------------------
void IniFileA::removeSection(const std::string& section)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::removeSection()" << std::endl;
#endif
	std::string sec = section;
	trim(sec);
	SecIndexA::iterator itr = _find_sec(sec);
	if (itr != mSections.end())
	{
		CELL_DELETE *itr;
		mSections.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniFileA::removeSection(IniSectionA* section)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::removeSection()" << std::endl;
#endif
	// No trim since internal object not from user
	SecIndexA::iterator itr = _find_sec(section->mSectionName);
	if( itr != mSections.end() )
	{
		CELL_DELETE *itr;
		mSections.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniFileA::removeAllSections(void)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::removeAllSections()" << std::endl;
#endif
	for (SecIndexA::iterator itr = mSections.begin(); itr != mSections.end(); ++itr)
	{
	#ifdef _INIFILE_DEBUG
		std::cout <<  "Deleting Section: IniSectionAName[" << (*itr)->getSectionName() << "]" << std::endl;
	#endif
		CELL_DELETE *itr;
	}
	mSections.clear();
}
//-----------------------------------------------------------------------
bool IniFileA::renameSection(const std::string& sectionName, const std::string& newSectionName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::renameSection()" << std::endl;
#endif
	// Note string trims are done in lower calls.
	bool rval = false;
	IniSectionA* pSec = getSection(sectionName);
	if (NULL != pSec)
	{
		rval = pSec->setSectionName(newSectionName);
	}
	return rval;
}
//-----------------------------------------------------------------------
bool IniFileA::renameKey(const std::string& sectionName, const std::string& keyName, const std::string& newKeyName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniFileA::renameKey()" << std::endl;
#endif
	// Note string trims are done in lower calls.
	bool rval = false;
	IniSectionA* pSec = getSection(sectionName);
	if (pSec != NULL)
	{
		IniKeyA* pKey = pSec->getKey(keyName);
		if (pKey != NULL)
			rval = pKey->setKeyName(newKeyName);
	}
	return rval;
}
//-----------------------------------------------------------------------
SecIndexA::const_iterator IniFileA::_find_sec(const std::string& section) const
{
	// Returns a constant iterator to a section by name, string is not trimmed
	IniSectionA bogus(NULL, section);
	return mSections.find(&bogus);
}
//-----------------------------------------------------------------------
SecIndexA::iterator IniFileA::_find_sec(const std::string& section)
{
	// Returns an iterator to a section by name, string is not trimmed
	IniSectionA bogus(NULL, section);
	return mSections.find(&bogus);
}

//-----------------------------------------------------------------------
IniFileA::ci_less_a::ci_less_a(void)
{
}
//-----------------------------------------------------------------------
IniFileA::ci_less_a::~ci_less_a(void)
{
}
//-----------------------------------------------------------------------
bool IniFileA::ci_less_a::operator() (const IniSectionA* s1, const IniSectionA* s2) const
{
#ifndef _WIN32
	return strcasecmp(s1->mSectionName.c_str(), s2->mSectionName.c_str()) < 0;
#else
	return _stricmp(s1->mSectionName.c_str(), s2->mSectionName.c_str()) < 0;
#endif
}


//-----------------------------------------------------------------------
IniMergeA::IniMergeA(IniFileA& ini) : mIni(ini)
{
}
//-----------------------------------------------------------------------
std::istream &IniMergeA::operator()(std::istream& input) const
{
	mIni.load(input, true);
	return input;
}

//-----------------------------------------------------------------------
IniSectionA::ci_less_a::ci_less_a(void)
{
}
//-----------------------------------------------------------------------
IniSectionA::ci_less_a::~ci_less_a(void)
{
}
//-----------------------------------------------------------------------
bool IniSectionA::ci_less_a::operator() (const IniKeyA* s1, const IniKeyA* s2) const
{
#ifndef _WIN32
	return strcasecmp(s1->mKeyName.c_str(), s2->mKeyName.c_str()) < 0;
#else
	return _stricmp(s1->mKeyName.c_str(), s2->mKeyName.c_str()) < 0;
#endif
}

//-----------------------------------------------------------------------
IniSectionA::IniSectionA(IniFileA* iniFile, const std::string& sectionName)
	: mIniFile(iniFile) , mSectionName(sectionName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::IniSectionA()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniSectionA::~IniSectionA()
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::~IniSectionA()" << std::endl;
#endif
	removeAllKeys();
}
//-----------------------------------------------------------------------
IniKeyA* IniSectionA::getKey(const std::string& keyName) const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::getKey()" << std::endl;
#endif
	std::string kn = keyName;
	trim(kn);
	KeyIndexA::const_iterator itr = _find_key(kn);
	if (itr != mKeys.end())
		return *itr;
	return NULL;
}
//-----------------------------------------------------------------------
void IniSectionA::removeAllKeys(void)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::removeAllKeys()" << std::endl;
#endif
	for (KeyIndexA::iterator itr = mKeys.begin(); itr != mKeys.end(); ++itr)
	{
	#ifdef _INIFILE_DEBUG
		std::cout <<  "Deleting Key: " << (*itr)->getKeyName() << std::endl;
	#endif
		CELL_DELETE *itr;
	}
	mKeys.clear();
}
//-----------------------------------------------------------------------
void IniSectionA::removeKey(const std::string& key)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::removeKey()" << std::endl;
#endif
	std::string k = key;
	trim(k);
	KeyIndexA::iterator itr = _find_key(k);
	if( itr != mKeys.end() )
	{
		CELL_DELETE *itr;
		mKeys.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniSectionA::removeKey(IniKeyA* key)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::removeKey()" << std::endl;
#endif
	// No trim is done to improve efficiency since IniKeyA* should already be trimmed
	KeyIndexA::iterator itr = _find_key(key->mKeyName);
	if (itr != mKeys.end())
	{
		CELL_DELETE *itr;
		mKeys.erase(itr);
	}
}
//-----------------------------------------------------------------------
IniKeyA* IniSectionA::addKey(const std::string& keyName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::addKey()" << std::endl;
#endif
	std::string key = keyName;
	trim(key);
	KeyIndexA::const_iterator itr = _find_key(key);
	if (itr == mKeys.end())
	{
		// Note constuctor doesn't trim the string so it is trimmed above
		IniKeyA* pKey = CELL_NEW IniKeyA(this , key);
		mKeys.insert(pKey);
		return pKey;
	}
	else
	{
		return *itr;
	}
}
//-----------------------------------------------------------------------
bool IniSectionA::setSectionName(const std::string& sectionName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::setSectionName()" << std::endl;
#endif
	std::string name = sectionName;
	trim(name);
	// Does this already exist.
	if (mIniFile->_find_sec(name) == mIniFile->mSections.end())
	{
	#ifdef _INIFILE_DEBUG
		std::cout <<  "Setting Section Name: [" << mSectionName <<  "] --> [" << name << "]" << std::endl;
	#endif

		// Find the current section if one exists and remove it since we are renaming
		SecIndexA::iterator itr = mIniFile->_find_sec(mSectionName);

		// Just to be safe make sure the old section exists
		if (itr != mIniFile->mSections.end())
			mIniFile->mSections.erase(itr);

		// Change name prior to ensure tree balance
		mSectionName = name;

		// Set the new map entry we know should not exist
		mIniFile->mSections.insert(this);

		return true;
	}
	else
	{
	#ifdef _INIFILE_DEBUG
		std::cout <<  "Section existed could not rename" << std::endl;
	#endif
		return false;
	}
}
//-----------------------------------------------------------------------
const std::string& IniSectionA::getSectionName() const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::getSectionName()" << std::endl;
#endif
	return mSectionName;
}
//-----------------------------------------------------------------------
const KeyIndexA& IniSectionA::getKeys() const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::getKeys()" << std::endl;
#endif
	return mKeys;
}
//-----------------------------------------------------------------------
std::string IniSectionA::getKeyValue(const std::string& key) const
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::GetKeyValue()" << std::endl;
#endif
	std::string value;
	IniKeyA* pKey = getKey(key);
	if (NULL != pKey)
	{
		value = pKey->getValue();
	}
	return value;
}
//-----------------------------------------------------------------------
void IniSectionA::setKeyValue(const std::string& key, const std::string& value)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::setKeyValue()" << std::endl;
#endif
	IniKeyA* pKey = addKey(key);
	if (pKey)
	{
		pKey->setValue(value);
	}
}
//-----------------------------------------------------------------------
KeyIndexA::const_iterator IniSectionA::_find_key(const std::string& key) const
{
	// Returns a constant iterator to a key by name, string is not trimmed
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::_find_key() const" << std::endl;
#endif
	IniKeyA bogus(NULL, key);
	return mKeys.find(&bogus);
}
//-----------------------------------------------------------------------
KeyIndexA::iterator IniSectionA::_find_key(const std::string& key)
{
	// Returns an iterator to a key by name, string is not trimmed
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniSectionA::_find_key()" << std::endl;
#endif
	IniKeyA bogus(NULL, key);
	return mKeys.find(&bogus);
}


//-----------------------------------------------------------------------
IniKeyA::IniKeyA(IniSectionA* section, const std::string& keyName)
	: mSection(section), mKeyName(keyName)
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniKeyA::IniKeyA()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniKeyA::~IniKeyA()
{
#ifdef _INIFILE_DEBUG
	std::cout <<  "IniKeyA::~IniKeyA()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
void IniKeyA::setValue(const std::string& value)
{
#ifdef _INIFILE_DEBUG
	std::cout << "IniKeyA::setValue()" << std::endl;
#endif
	mValue = value;
}
//-----------------------------------------------------------------------
const std::string& IniKeyA::getValue() const
{
#ifdef _INIFILE_DEBUG
	std::cout << "IniKeyA::getValue()" << std::endl;
#endif
	return mValue;
}
//-----------------------------------------------------------------------
bool IniKeyA::setKeyName(const std::string& keyName)
{
#ifdef _INIFILE_DEBUG
	std::cout << "IniKeyA::setKeyName()" << std::endl;
#endif
	std::string name = keyName;
	trim(name);

	// Check for key name conflict
	if (mSection->_find_key(name) == mSection->mKeys.end())
	{
		KeyIndexA::iterator itr = mSection->_find_key(mKeyName);

		// Find the old map entry and remove it
		if (itr != mSection->mKeys.end())
			mSection->mKeys.erase(itr);

		// Change name prior to ensure tree balance
		mKeyName = name;

		// Make the new map entry
		mSection->mKeys.insert(this);
		return true;
	}
	else
	{
	#ifdef _INIFILE_DEBUG
		std::cout << "Could not set key name, key by that name already exists!" << std::endl;
	#endif
		return false;
	}
}
//-----------------------------------------------------------------------
const std::string& IniKeyA::getKeyName() const
{
#ifdef _INIFILE_DEBUG
	std::cout << "IniKeyA::getKeyName()" << std::endl;
#endif
	return mKeyName;
}

} // end namespace cell


/////////////////////////////////////////////////////////////////////////////////////////////////////////////


// Helper Functions
void rtrim(std::wstring &str, const std::wstring& chars = L" \t")
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"rtrim()" << std::endl;
#endif
	str.erase(str.find_last_not_of(chars)+1);
}

void ltrim(std::wstring &str, const std::wstring& chars = L" \t")
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"LTrim()" << std::endl;
#endif
	str.erase(0, str.find_first_not_of(chars));
}

void trim(std::wstring& str , const std::wstring& chars = L" \t")
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"trim()" << std::endl;
#endif
	str.erase(str.find_last_not_of(chars)+1);
	str.erase(0, str.find_first_not_of(chars));
}

// Stream Helpers
std::wostream& operator<<(std::wostream& output, cell::IniFileW& obj)
{
	obj.save(output);
	return output;
}

std::wistream& operator>>(std::wistream& input, cell::IniFileW& obj)
{
	obj.load(input);
	return input;
}

std::wistream& operator>>(std::wistream& input, cell::IniMergeW merger)
{
	return merger(input);
}

namespace cell
{

const wchar_t* const IniFileW::LF = _CRLFW;

//-----------------------------------------------------------------------
IniFileW::IniFileW(void)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::IniFileW()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniFileW::~IniFileW(void)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::~IniFileW()" << std::endl;
#endif
	removeAllSections();
}
//-----------------------------------------------------------------------
void IniFileW::save(std::wostream& output)
{
	std::wstring section;

	for (SecIndexW::iterator itr = mSections.begin(); itr != mSections.end(); ++itr)
	{
		section = L"[" + (*itr)->getSectionName() + L"]";

	#ifdef _INIFILE_DEBUG
		std::wcout <<  L"Writing Section " << section << std::endl;
	#endif

		output << section << _CRLFA;

		for (KeyIndexW::iterator klitr = (*itr)->mKeys.begin(); klitr != (*itr)->mKeys.end(); ++klitr)
		{
			std::wstring key = (*klitr)->getKeyName() + L"=" + (*klitr)->getValue();
		#ifdef _INIFILE_DEBUG
			std::wcout <<  L"Writing Key [" << key << L"]" << std::endl;
		#endif
			output << key << _CRLFA;
		}
	}
}
//-----------------------------------------------------------------------
bool IniFileW::save(const std::wstring& fileName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::save() - " << fileName << std::endl;
#endif

	std::wofstream output;

#ifndef _WIN32
	output.open(wstr_to_str(fileName).c_str() , std::ios::binary);
#else
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
	output.open(wstr_to_str(fileName).c_str() , std::ios::binary);
#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
	output.open(fileName.c_str() , std::ios::binary);
#endif
#endif

	if (!output.is_open())
		return false;

	save(output);

	output.close();
	return true;
}
//-----------------------------------------------------------------------
void IniFileW::load(std::wistream& input , bool merge)
{
	if (!merge)
		removeAllSections();

	IniSectionW* section = NULL;
	std::wstring read;
	enum { KEY , SECTION , COMMENT , OTHER };

	while (std::getline(input, read))
	{
		// Trim all whitespace on the left
		ltrim(read);
		// Trim any returns
		rtrim(read, L"\n\r");

		if (!read.empty())
		{
			unsigned int nType = ( read.find_first_of(L"[") == 0 && ( read[read.find_last_not_of(L" \t\r\n")] == L']' ) ) ? SECTION : OTHER ;
			nType = ( (nType == OTHER) && ( read.find_first_of(L"=") != std::wstring::npos && read.find_first_of(L"=") > 0 ) ) ? KEY : nType ;
			nType = ( (nType == OTHER) && ( read.find_first_of(L"#") == 0) ) ? COMMENT : nType ;

			switch (nType)
			{
			case SECTION:
			#ifdef _INIFILE_DEBUG
				std::wcout <<  L"Parsing: Secton - " << read << std::endl;
			#endif
				section = addSection(read.substr(1 , read.size() - 2));
				break;

			case KEY:
				{
				#ifdef _INIFILE_DEBUG
					std::wcout <<  L"Parsing: Key - " << read << std::endl;
				#endif
					// Check to ensure valid section... or drop the keys listed
					if (NULL != section)
					{
						size_t iFind = read.find_first_of(L"=");
						std::wstring key = read.substr(0, iFind);
						std::wstring value = read.substr(iFind + 1);
						IniKeyW* pKey = section->addKey(key);
						if (pKey)
						{
						#ifdef _INIFILE_DEBUG
							std::wcout <<  L"Parsing: Key Value - " << value << std::endl;
						#endif
							pKey->setValue(value);
						}
					}
				}
				break;
			case COMMENT:
			#ifdef _INIFILE_DEBUG
				std::wcout <<  L"Parsing: Comment - " << read << std::endl;
			#endif
				break;
			case OTHER:
			#ifdef _INIFILE_DEBUG
				std::wcout <<  L"Parsing: Other - " << read << std::endl;
			#endif
				break;
			}
		}
	}
}
//-----------------------------------------------------------------------
bool IniFileW::load(const std::wstring& fileName , bool merge)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::load() - " << fileName << std::endl;
#endif

	std::wifstream input;

#ifndef _WIN32
	input.open(wstr_to_str(fileName).c_str() , std::ios::binary);
#else
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
	input.open(wstr_to_str(fileName).c_str() , std::ios::binary);
#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
	input.open(fileName.c_str() , std::ios::binary);
#endif
#endif

	if (!input.is_open())
		return false;

	load(input, merge);

	input.close();
	return true;
}
//-----------------------------------------------------------------------
const SecIndexW& IniFileW::getSections(void) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::getSections()" << std::endl;
#endif
	return mSections;
}
//-----------------------------------------------------------------------
IniSectionW* IniFileW::getSection(const std::wstring& section) const
{
#ifdef _INIFILE_DEBUG
	std::wcout << L"IniFileW::getSection()" << std::endl;
#endif
	std::wstring sec = section;
	trim(sec);
	SecIndexW::const_iterator itr = _find_sec(sec);
	if (itr != mSections.end())
		return *itr;
	return NULL;
}
//-----------------------------------------------------------------------
IniSectionW* IniFileW::addSection(const std::wstring& section)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::addSection()" << std::endl;
#endif
	std::wstring sec = section;
	trim(sec);
	SecIndexW::const_iterator itr = _find_sec(sec);
	if (itr == mSections.end())
	{
		// Note constuctor doesn't trim the string so it is trimmed above
		IniSectionW* pSection = CELL_NEW IniSectionW(this , sec);
		mSections.insert(pSection);
		return pSection;
	}
	else
	{
		return *itr;
	}
}
//-----------------------------------------------------------------------
std::wstring IniFileW::getKeyValue(const std::wstring& section, const std::wstring& key) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::getKeyValue()" << std::endl;
#endif
	std::wstring value;
	IniSectionW* pSec = getSection(section);
	if (pSec)
	{
		IniKeyW* pKey = pSec->getKey(key);
		if (pKey)
			value = pKey->getValue();
	}
	return value;
}
//-----------------------------------------------------------------------
void IniFileW::setKeyValue(const std::wstring& section, const std::wstring& key, const std::wstring& value)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::setKeyValue()" << std::endl;
#endif
	IniSectionW* pSec = addSection(section);
	if (pSec)
	{
		IniKeyW* pKey = pSec->addKey(key);
		if (pKey)
			pKey->setValue(value);
	}
}
//-----------------------------------------------------------------------
void IniFileW::removeSection(const std::wstring& section)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::removeSection()" << std::endl;
#endif
	std::wstring sec = section;
	trim(sec);
	SecIndexW::iterator itr = _find_sec(sec);
	if (itr != mSections.end())
	{
		CELL_DELETE *itr;
		mSections.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniFileW::removeSection(IniSectionW* section)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::removeSection()" << std::endl;
#endif
	// No trim since internal object not from user
	SecIndexW::iterator itr = _find_sec(section->mSectionName);
	if (itr != mSections.end())
	{
		CELL_DELETE *itr;
		mSections.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniFileW::removeAllSections(void)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::removeAllSections()" << std::endl;
#endif
	for (SecIndexW::iterator itr = mSections.begin(); itr != mSections.end(); ++itr)
	{
	#ifdef _INIFILE_DEBUG
		std::wcout <<  L"Deleting Section: IniSectionWName[" << (*itr)->getSectionName() << L"]" << std::endl;
	#endif
		CELL_DELETE *itr;
	}
	mSections.clear();
}
//-----------------------------------------------------------------------
bool IniFileW::renameSection(const std::wstring& sectionName, const std::wstring& newSectionName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::renameSection()" << std::endl;
#endif
	// Note string trims are done in lower calls.
	bool rval = false;
	IniSectionW* pSec = getSection(sectionName);
	if (pSec)
	{
		rval = pSec->setSectionName(newSectionName);
	}
	return rval;
}
//-----------------------------------------------------------------------
bool IniFileW::renameKey(const std::wstring& sectionName, const std::wstring& keyName, const std::wstring& newKeyName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniFileW::renameKey()" << std::endl;
#endif
	// Note string trims are done in lower calls.
	bool rval = false;
	IniSectionW* pSec = getSection(sectionName);
	if (pSec != NULL)
	{
		IniKeyW* pKey = pSec->getKey(keyName);
		if (pKey != NULL)
			rval = pKey->setKeyName(newKeyName);
	}
	return rval;
}
//-----------------------------------------------------------------------
SecIndexW::const_iterator IniFileW::_find_sec(const std::wstring& section) const
{
	// Returns a constant iterator to a section by name, string is not trimmed
	IniSectionW bogus(NULL, section);
	return mSections.find(&bogus);
}
//-----------------------------------------------------------------------
SecIndexW::iterator IniFileW::_find_sec(const std::wstring& section)
{
	// Returns an iterator to a section by name, string is not trimmed
	IniSectionW bogus(NULL, section);
	return mSections.find(&bogus);
}


//-----------------------------------------------------------------------
IniFileW::IniMergeW::IniMergeW(IniFileW& ini) : mIni(ini)
{
}
//-----------------------------------------------------------------------
std::wistream &IniFileW::IniMergeW::operator()(std::wistream& input) const
{
	mIni.load(input, true);
	return input;
}


//-----------------------------------------------------------------------
IniFileW::ci_less_w::ci_less_w(void)
{
}
//-----------------------------------------------------------------------
IniFileW::ci_less_w::~ci_less_w(void)
{
}
//-----------------------------------------------------------------------
bool IniFileW::ci_less_w::operator() (const IniSectionW* s1, const IniSectionW* s2) const
{
#ifndef _WIN32
	return wcscasecmp(s1->mSectionName.c_str(), s2->mSectionName.c_str()) < 0;
#else
	return _wcsicmp(s1->mSectionName.c_str(), s2->mSectionName.c_str()) < 0;
#endif
}


//-----------------------------------------------------------------------
IniSectionW::IniSectionW(IniFileW* iniFile, const std::wstring& sectionName)
	: mIniFile(iniFile), mSectionName(sectionName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::IniSectionW()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniSectionW::~IniSectionW()
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::~IniSectionW()" << std::endl;
#endif
	removeAllKeys();
}
//-----------------------------------------------------------------------
IniKeyW* IniSectionW::getKey(const std::wstring& keyName) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::getKey()" << std::endl;
#endif
	std::wstring name = keyName;
	trim(name);
	KeyIndexW::const_iterator itr = _find_key(name);
	if (itr != mKeys.end())
		return *itr;
	return NULL;
}
//-----------------------------------------------------------------------
void IniSectionW::removeAllKeys()
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::removeAllKeys()" << std::endl;
#endif
	for (KeyIndexW::iterator itr = mKeys.begin(); itr != mKeys.end(); ++itr)
	{
	#ifdef _INIFILE_DEBUG
		std::wcout <<  L"Deleting Key: " << (*itr)->getKeyName() << std::endl;
	#endif
		CELL_DELETE *itr;
	}
	mKeys.clear();
}
//-----------------------------------------------------------------------
void IniSectionW::removeKey(const std::wstring& key)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::removeKey()" << std::endl;
#endif
	std::wstring name = key;
	trim(name);
	KeyIndexW::iterator itr = _find_key(name);
	if (itr != mKeys.end())
	{
		CELL_DELETE *itr;
		mKeys.erase(itr);
	}
}
//-----------------------------------------------------------------------
void IniSectionW::removeKey(IniKeyW* key)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::removeKey()" << std::endl;
#endif
	// No trim is done to improve efficiency since IniKeyW* should already be trimmed
	KeyIndexW::iterator itr = _find_key(key->mKeyName);
	if (itr != mKeys.end())
	{
		CELL_DELETE *itr;
		mKeys.erase(itr);
	}
}
//-----------------------------------------------------------------------
IniKeyW* IniSectionW::addKey(const std::wstring& keyName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::addKey()" << std::endl;
#endif
	std::wstring name = keyName;
	trim(name);
	KeyIndexW::const_iterator itr = _find_key(name);
	if (itr == mKeys.end())
	{
		// Note constuctor doesn't trim the string so it is trimmed above
		IniKeyW* pKey = CELL_NEW IniKeyW(this, name);
		mKeys.insert(pKey);
		return pKey;
	}
	else
	{
		return *itr;
	}
}
//-----------------------------------------------------------------------
bool IniSectionW::setSectionName(const std::wstring& sectionName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::setSectionName()" << std::endl;
#endif
	std::wstring name = sectionName;
	trim(name);
	// Does this already exist.
	if (mIniFile->_find_sec(name) == mIniFile->mSections.end())
	{
	#ifdef _INIFILE_DEBUG
		std::wcout <<  L"Setting Section Name: [" << mSectionName <<  L"] --> [" << name << L"]" << std::endl;
	#endif

		// Find the current section if one exists and remove it since we are renaming
		SecIndexW::iterator itr = mIniFile->_find_sec(name);

		// Just to be safe make sure the old section exists
		if (itr != mIniFile->mSections.end())
			mIniFile->mSections.erase(itr);

		// Change name prior to ensure tree balance
		mSectionName = name;

		// Set the new map entry we know should not exist
		mIniFile->mSections.insert(this);

		return true;
	}
	else
	{
	#ifdef _INIFILE_DEBUG
		std::wcout <<  L"Section existed could not rename" << std::endl;
	#endif
		return false;
	}
}
//-----------------------------------------------------------------------
const std::wstring& IniSectionW::getSectionName(void) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::getSectionName()" << std::endl;
#endif
	return mSectionName;
}
//-----------------------------------------------------------------------
const KeyIndexW& IniSectionW::getKeys(void) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::getKeys()" << std::endl;
#endif
	return mKeys;
}
//-----------------------------------------------------------------------
std::wstring IniSectionW::getKeyValue(const std::wstring& key) const
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::getKeyValue()" << std::endl;
#endif
	std::wstring value;
	IniKeyW* pKey = getKey(key);
	if (pKey)
	{
		value = pKey->getValue();
	}
	return value;
}
//-----------------------------------------------------------------------
void IniSectionW::setKeyValue(const std::wstring& key, const std::wstring& value)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::setKeyValue()" << std::endl;
#endif
	IniKeyW* pKey = addKey(key);
	if (pKey)
	{
		pKey->setValue(value);
	}
}
//-----------------------------------------------------------------------
KeyIndexW::const_iterator IniSectionW::_find_key(const std::wstring& key) const
{
	// Returns a constant iterator to a key by name, string is not trimmed
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::_find_key() const" << std::endl;
#endif
	IniKeyW bogus(NULL, key);
	return mKeys.find(&bogus);
}
//-----------------------------------------------------------------------
KeyIndexW::iterator IniSectionW::_find_key(const std::wstring& key)
{
	// Returns an iterator to a key by name, string is not trimmed
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniSectionW::_find_key()" << std::endl;
#endif
	IniKeyW bogus(NULL, key);
	return mKeys.find(&bogus);
}

//-----------------------------------------------------------------------
IniSectionW::ci_less_w::ci_less_w(void)
{
}
//-----------------------------------------------------------------------
IniSectionW::ci_less_w::~ci_less_w(void)
{
}
//-----------------------------------------------------------------------
bool IniSectionW::ci_less_w::operator() (const IniKeyW* s1, const IniKeyW* s2) const
{
#ifndef _WIN32
	return wcscasecmp(s1->mKeyName.c_str(), s2->mKeyName.c_str()) < 0;
#else
	return _wcsicmp(s1->mKeyName.c_str(), s2->mKeyName.c_str()) < 0;
#endif
}


//-----------------------------------------------------------------------
IniKeyW::IniKeyW(IniSectionW* section, const std::wstring& keyName)
	: mSection(section), mKeyName(keyName)
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniKeyW::IniKeyW()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
IniKeyW::~IniKeyW()
{
#ifdef _INIFILE_DEBUG
	std::wcout <<  L"IniKeyW::~IniKeyW()" << std::endl;
#endif
}
//-----------------------------------------------------------------------
void IniKeyW::setValue(const std::wstring& value)
{
#ifdef _INIFILE_DEBUG
	std::wcout << L"IniKeyW::setValue()" << std::endl;
#endif
	mValue = value;
}
//-----------------------------------------------------------------------
const std::wstring& IniKeyW::getValue() const
{
#ifdef _INIFILE_DEBUG
	std::wcout << L"IniKeyW::getValue()" << std::endl;
#endif
	return mValue;
}
//-----------------------------------------------------------------------
bool IniKeyW::setKeyName(const std::wstring& keyName)
{
#ifdef _INIFILE_DEBUG
	std::wcout << L"IniKeyW::setKeyName()" << std::endl;
#endif
	std::wstring name = keyName;
	trim(name);

	// Check for key name conflict
	if (mSection->_find_key(name) == mSection->mKeys.end())
	{
		KeyIndexW::iterator itr = mSection->_find_key(name);

		// Find the old map entry and remove it
		if (itr != mSection->mKeys.end())
			mSection->mKeys.erase(itr);

		// Change name prior to ensure tree balance
		mKeyName = name;

		// Make the new map entry
		mSection->mKeys.insert(this);
		return true;
	}
	else
	{
	#ifdef _INIFILE_DEBUG
		std::wcout << L"Could not set key name, key by that name already exists!" << std::endl;
	#endif
		return false;
	}
}
//-----------------------------------------------------------------------
const std::wstring& IniKeyW::getKeyName() const
{
#ifdef _INIFILE_DEBUG
	std::wcout << L"IniKeyW::getKeyName()" << std::endl;
#endif
	return mKeyName;
}

} // end namespace cell
